package service

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"gitee.com/tools/internal/web/tools/common"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/sha3"
	hash2 "hash"
	"io"
	"math/rand"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

type Encrypt interface {
	Md5Salt(ctx context.Context, password, salt string) (string, error)
	Md5File(ctx context.Context, path string) (string, error)
	Bcrypt(ctx context.Context, pass string) (string, error)
	Base64Plus(ctx context.Context, data string) (string, error)
	Base64(ctx context.Context, data string) (string, error)
	UrlEncode(ctx context.Context, data string) (string, error)
	UrlUncode(ctx context.Context, data string) (string, error)
	AesEncode(ctx context.Context, content, mode, paddingMode string, key []byte) ([]byte, error)
	AesUncode(ctx context.Context, cipherText []byte, mode, paddingMode string, key []byte) (string, error)
	ObfuscatePHPCode(ctx context.Context, phpCode string, aesKey []byte) (string, error)
	ShaEncrypt(ctx context.Context, encryptType string, data []byte) (string, error)
}
type EncryptTools struct {
	lock sync.Mutex
}

func (e *EncryptTools) ShaEncrypt(ctx context.Context, encryptType string, data []byte) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	var hash hash2.Hash
	switch encryptType {
	case "sha1":
		hash = sha1.New()
	case "sha3":
		hash = sha3.New512()
	case "sha256":
		hash = sha256.New()
	case "sha512":
		hash = sha512.New()
	default:
		return "", errors.New("加密类型错误")
	}
	hash.Write(data)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		byteHash := hash.Sum(nil)
		return fmt.Sprintf("%x", byteHash), nil
	}
}
func (e *EncryptTools) ObfuscatePHPCode(ctx context.Context, phpCode string, aesKey []byte) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()

	// 替换变量名
	lines := strings.Split(phpCode, "\n")
	variableNameMap := make(map[string]string)
	for i, line := range lines {
		words := strings.Fields(line)
		for j, word := range words {
			if strings.HasPrefix(word, "$") {
				newVariableName := common.GenerateSalt(8)
				variableNameMap[word] = newVariableName
				words[j] = newVariableName
			}
		}
		lines[i] = strings.Join(words, " ")
	}

	// 替换函数名:
	functionNameMap := make(map[string]string)
	for i, line := range lines {
		words := strings.Fields(line)
		for j, word := range words {
			if len(word) > 0 && strings.HasPrefix(word, "function") && strings.Contains(word, "(") {
				newFunctionName := common.GenerateSalt(10)
				functionNameMap[word] = newFunctionName
				words[j] = "function" + newFunctionName + "("
			}
		}
		lines[i] = strings.Join(words, " ")
	}

	// 加密字符串常量
	encryptedLines := make([]string, len(lines))
	for i, line := range lines {
		words := strings.Fields(line)
		encryptedWords := make([]string, len(words))
		for j, word := range words {
			if strings.HasPrefix(word, "'") && strings.HasSuffix(word, "'") ||
				strings.HasPrefix(word, "\"") && strings.HasSuffix(word, "\"") {
				encryptedWord, err := e.AesEncode(ctx, word[1:len(word)-1], "CBC", "iso", aesKey)
				if err != nil {
					return "", err
				}
				encryptedWords[j] = fmt.Sprintf("'%s'", string(encryptedWord))
			} else {
				encryptedWords[j] = word
			}
		}
		encryptedLines[i] = strings.Join(encryptedWords, " ")
	}

	// 打乱代码结构
	for i := range encryptedLines {
		j := rand.Intn(len(encryptedLines))
		encryptedLines[i], encryptedLines[j] = encryptedLines[j], encryptedLines[i]
	}
	return strings.Join(encryptedLines, "\n") + "\n" + fmt.Sprintf("VariableNameMap: %vFunctionNameMap: %v", variableNameMap, functionNameMap), nil
}

func (e *EncryptTools) AesUncode(ctx context.Context, cipherText []byte, mode, paddingMode string, key []byte) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()

	// 创建aes解密块
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	// 创建初始向量(IV), 偏移量
	iv := make([]byte, block.BlockSize())

	var m cipher.BlockMode
	var stram cipher.Stream
	var plainText []byte
	//创建加密模式
	switch mode {
	case "CBC":
		m = cipher.NewCBCDecrypter(block, iv)
		plainText = make([]byte, len(cipherText))
		m.CryptBlocks(plainText, cipherText)
		break
	case "CFB":
		stram = cipher.NewCFBDecrypter(block, iv)
		plainText = make([]byte, len(cipherText))
		stram.XORKeyStream(plainText, cipherText)
		break
	case "OFB":
		stram = cipher.NewOFB(block, iv)
		plainText = make([]byte, len(cipherText))
		stram.XORKeyStream(plainText, cipherText)
		break
	case "ECB":
		plainText = make([]byte, len(cipherText))
		for i := 0; i < len(cipherText); i += block.BlockSize() {
			block.Decrypt(plainText[i:i+block.BlockSize()], cipherText[i:i+block.BlockSize()])
		}
		break
	default:
		return "", errors.New("加密模式错误, 加密失败")
	}
	// 向加密块中填充明文
	plainText, err = common.NewUnPadResult(plainText, paddingMode)
	return string(plainText), err
}

func (e *EncryptTools) AesEncode(ctx context.Context, content, mode, paddingMode string, key []byte) ([]byte, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	// 创建aes加密块
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	// 向加密块中填充明文
	plainText, err := common.NewPadResult([]byte(content), paddingMode, block.BlockSize())
	if err != nil {
		return nil, err
	}

	// 创建初始向量(IV), 偏移量
	iv := make([]byte, block.BlockSize())

	var m cipher.BlockMode
	var stram cipher.Stream
	var ciphertext []byte
	//创建加密模式
	switch mode {
	case "CBC":
		m = cipher.NewCBCEncrypter(block, iv)
		ciphertext = make([]byte, len(plainText))
		m.CryptBlocks(ciphertext, plainText)
		break
	case "CFB":
		stram = cipher.NewCFBEncrypter(block, iv)
		ciphertext = make([]byte, len(plainText))
		stram.XORKeyStream(ciphertext, plainText)
		break
	case "OFB":
		stram = cipher.NewOFB(block, iv)
		ciphertext = make([]byte, len(plainText))
		stram.XORKeyStream(ciphertext, plainText)
		break
	case "ECB":
		ciphertext = make([]byte, len(plainText))
		for i := 0; i < len(plainText); i += block.BlockSize() {
			block.Encrypt(ciphertext[i:i+block.BlockSize()], plainText[i:i+block.BlockSize()])
		}
		break
	default:
		return nil, errors.New("加密模式错误, 加密失败")
	}
	return ciphertext, nil
}

func (e *EncryptTools) UrlEncode(ctx context.Context, data string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*1)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	encodeData := url.QueryEscape(data)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		return encodeData, nil
	}
}

func (e *EncryptTools) UrlUncode(ctx context.Context, data string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*1)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	encodeData, err := url.QueryUnescape(data)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		return encodeData, err
	}
}

func (e *EncryptTools) Bcrypt(ctx context.Context, pass string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	val, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		return fmt.Sprintf("%x", val), err
	}
}

func (e *EncryptTools) Md5File(ctx context.Context, path string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		md5Val := hash.Sum(nil)
		return fmt.Sprintf("%x", md5Val), err
	}
}

func (e *EncryptTools) Md5Salt(ctx context.Context, password, salt string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*3)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	hash := md5.New()
	_, err := io.WriteString(hash, password+salt)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		md5Val := hash.Sum(nil)
		return fmt.Sprintf("%x", md5Val), err
	}
}

func (e *EncryptTools) Base64Plus(ctx context.Context, data string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*1)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	val := base64.StdEncoding.EncodeToString([]byte(data))
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		return val, nil
	}
}

func (e *EncryptTools) Base64(ctx context.Context, data string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*1)
	defer cancel()
	e.lock.Lock()
	defer e.lock.Unlock()
	encodeData, err := base64.StdEncoding.DecodeString(data)
	select {
	case <-ctx.Done():
		return "", errors.New("计算超时")
	default:
		return string(encodeData), err
	}
}

func NewEncryptTools() Encrypt {
	return &EncryptTools{}
}
